/**
 * This is is a cache prefetch test with a vector multiplication.
 *
 * In most of the platform, prefetch won't do any good. Because the hardware
 * prefetch predict mechanism will do the bidding.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include "misc.h"

//#define ALGO_TEST

#ifdef ALGO_TEST
#define W 30
#define H 40
#define fetchlog(rw, data, i1, i2) \
	printf(rw "-prefetch " data "[%d][%d]\n", (int)(i1), (int)(i2))
#else
#define fetchlog(...)
#define W 1000
#define H 200
#endif

#ifndef CACHE_LINE_SIZE
#warning "Assume cacheline size as 64"
#define CACHE_LINE_SIZE 64
#endif

#define INT_PER_CACHELINE (CACHE_LINE_SIZE/sizeof(int))

#define PF_READ 0
#define PF_WRITE 1

int a[W][H];
int b[H][W];
int c[W][W];

void calpf(void *d) {
	int i, j, k;

	for (i=0; i<W; i++) {
		for(j=0; j<W; j++) {
			c[i][j] = 0;
#ifdef PREFETCH
			if (!(j%INT_PER_CACHELINE)) {
				__builtin_prefetch((const void *)&c[i][j+INT_PER_CACHELINE], PF_WRITE, 3);
				fetchlog("w", "c", i, j+INT_PER_CACHELINE);
			}
#endif
			for (k=0; k<H; k++) {
				c[i][j] += a[i][k]*b[k][j];
#ifdef PREFETCH
				if (!j && !(k%INT_PER_CACHELINE)) {
					__builtin_prefetch((const void *)&a[i][k+INT_PER_CACHELINE], PF_READ, 3);
					fetchlog("r", "a", i, k+INT_PER_CACHELINE);
				}
				if (!i && !(j%INT_PER_CACHELINE)) {
					__builtin_prefetch((const void *)&b[k][j+INT_PER_CACHELINE], PF_READ, 3);
					fetchlog("r", "b", k, j+INT_PER_CACHELINE);
				}
#endif
			}

		}
	}
}

void calnopf(void *d) {
	int i, j, k;

	for (i=0; i<W; i++) {
		for(j=0; j<W; j++) {
			c[i][j] = 0;

			for (k=0; k<H; k++) {
				c[i][j] += a[i][k]*b[k][j];
			}

		}
	}
}

int main(void) {
	int i, j;

	for(i=0; i<W; i++) {
		for(j=0; j<H; j++) {
			a[i][j] = rand();
			b[j][i] = rand();
		}
	}

	for(i=0; i<W; i++)
		for(j=0; j<W; j++)
			c[i][j] = rand();

	for (i = 0; i < 3; i++) {
		__builtin___clear_cache(a, &a[W][H]);
		__builtin___clear_cache(b, &b[H][W]);
		__builtin___clear_cache(c, &c[W][W]);

		MEASURE_TIME(calnopf, NULL);

		__builtin___clear_cache(a, &a[W][H]);
		__builtin___clear_cache(b, &b[H][W]);
		__builtin___clear_cache(c, &c[W][W]);

		MEASURE_TIME(calpf, NULL);
	}

	return 0;
}
